#include <fcntl.h>
#include "exceptions.h"
#include <unistd.h>
#include "storage.h"
#include <cassert>
#include <cstddef>

namespace torrent {

Storage::Storage() :
  m_fd(-1),
  m_blockSize(0),
  m_offset(0) {
	clear();
}

Storage::~Storage() {
	close();
}

bool Storage::open(const std::string& filename, unsigned int blockSize,
		unsigned int offset, bool writeAccess, bool createFile,
		unsigned int mask) {
	assert(sizeof(off_t) == 8);

	if (offset >= blockSize ||
		blockSize == 0)
		throw internal_error("Storage received bad block size of offset");

	close();

	m_blockSize = blockSize;
	m_offset = offset;
	m_write = writeAccess;

	m_fd = ::open(filename.c_str(),
			O_LARGEFILE |
			(writeAccess ? O_RDWR : O_RDONLY) |
			(createFile ? O_CREAT : 0),
			mask);

	if (m_fd == -1)
		return writeAccess ? open(filename, blockSize, offset, false, createFile, mask) : false;

	fstat(m_fd, &m_stat);
	m_blocks = BlockVector(blockCount(), NULL);

	return true;
}

void Storage::close() {
	if (m_fd == -1)
		return;

	::close(m_fd);
	clear();
	m_blocks.resize(0);

	m_fd = -1;
}

void Storage::clear() {
	for (BlockVector::iterator itr = m_blocks.begin(); itr != m_blocks.end(); ++itr)
		if (*itr) {
			(*itr)->m_ptr = NULL;
			*itr = NULL;
		}
}

bool Storage::isWrite() const {
	return m_write;
}

bool Storage::isOpen() const {
	return m_fd != -1;
}

uint64_t Storage::fileSize() const {
	return m_stat.st_size;
}

unsigned int Storage::blockCount() const {
	return fileSize() ? (fileSize() - 1 + m_offset) / m_blockSize + 1 : 0;
}

} // namespace torrent
